﻿using Microsoft.AspNetCore.Mvc;
using Plugin.Api.Infrastructure;
using Plugin.Api.Models.Catalog;
using Plugin.Api.Models.Media;
using Plugin.Api.Models.Goods;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Urs.Core;
using Urs.Core.Caching;
using Urs.Data.Domain.Configuration;
using Urs.Data.Domain.Stores;
using Urs.Framework.Controllers;
using Urs.Services.Media;
using Urs.Services.Orders;
using Urs.Services.Security;
using Urs.Services.Stores;
using Urs.Services.Users;

namespace Plugin.Api.Controllers
{
    /// <summary>
    /// 商品接口
    /// </summary>
    [ApiVersion("1.0")]
    [Route("api/v{version:apiVersion}/goods")]
    [ApiController]
    public class GoodsController : BaseApiController
    {
        private readonly ICategoryService _categoryService;
        private readonly IUserService _userService;
        private readonly IGoodsSpecFormatter _goodsSpecFormatter;
        private readonly IGoodsSpecParser _goodsSpecParser;
        private readonly MediaSettings _mediaSettings;
        private readonly StoreInformationSettings _storeInformationSettings;
        private readonly IGoodsService _goodsService;
        private readonly IGoodsTagService _goodsTagService;
        private readonly IPermissionService _permissionService;
        private readonly ICacheManager _cacheManager;
        private readonly IPictureService _pictureService;
        private readonly IWebHelper _webHelper;
        private readonly IGoodsParameterService _goodsParameterService;
        private readonly IPriceCalculationService _priceCalculationService;
        private readonly IGoodsSpecService _goodsSpecService;
        private readonly IOrderReportService _orderReportService;
        /// <summary>
        /// 构造器
        /// </summary>
        public GoodsController(ICategoryService categoryService,
            IUserService userService,
            IGoodsSpecFormatter goodsSpecFormatter,
            IGoodsSpecParser goodsSpecParser,
            MediaSettings mediaSettings,
            StoreInformationSettings storeInformationSettings,
            IGoodsService goodsService,
            IGoodsTagService goodsTagService,
            IPermissionService permissionService,
            ICacheManager cacheManager,
            IPictureService pictureService,
            IWebHelper webHelper,
            IGoodsParameterService goodsParameterService,
            IPriceCalculationService priceCalculationService,
            IGoodsSpecService goodsSpecService,
            IOrderReportService orderReportService)
        {
            this._categoryService = categoryService;
            this._userService = userService;
            this._goodsSpecFormatter = goodsSpecFormatter;
            this._goodsSpecParser = goodsSpecParser;
            this._mediaSettings = mediaSettings;
            this._storeInformationSettings = storeInformationSettings;
            this._goodsService = goodsService;
            this._goodsTagService = goodsTagService;
            this._permissionService = permissionService;
            this._cacheManager = cacheManager;
            this._pictureService = pictureService;
            this._webHelper = webHelper;
            this._goodsParameterService = goodsParameterService;
            this._priceCalculationService = priceCalculationService;
            this._goodsSpecService = goodsSpecService;
            this._orderReportService = orderReportService;
        }
        #region Utilities

        [NonAction]
        protected List<int> GetChildCategoryIds(int parentCategoryId, bool showHidden = false)
        {
                var categoriesIds = new List<int>();
                var categories = _categoryService.GetAllCategoriesByParentCategoryId(parentCategoryId, showHidden);
                foreach (var category in categories)
                {
                    categoriesIds.Add(category.Id);
                    categoriesIds.AddRange(GetChildCategoryIds(category.Id, showHidden));
                }
                return categoriesIds;
           
        }

        [NonAction]
        protected IEnumerable<MoGoodsOverview> PrepareGoodsOverviewModels(IEnumerable<Goods> list,
            bool preparePriceModel = true, bool preparePictureModel = true,
            int? goodsThumbPictureSize = null, bool prepareGoodsParameters = false, bool prepareGoodsTag = false,
            bool forceRedirectionAfterAddingToCart = false, bool prepareFullDescription = false)
        {
            if (list == null)
                throw new ArgumentNullException("list");

            var models = new List<MoGoodsOverview>();
            foreach (var goods in list)
            {
                var model = new MoGoodsOverview()
                {
                    Id = goods.Id,
                    Sku = goods.Sku,
                    Name = goods.Name,
                    Short = goods.ShortDescription,
                    Qty = goods.StockQuantity,
                    Published = goods.Published,
                    SaleVolume = goods.SaleVolume
                };
                if (prepareFullDescription)
                {
                    model.Full = goods.FullDescription;
                }
                //price
                if (preparePriceModel)
                {
                    #region Prepare goods price

                    var priceModel = new MoGoodsOverview.MoPrice();
                    priceModel.OldPrice = PriceFormatter.FormatPrice(goods.OldPrice);
                    priceModel.Price = PriceFormatter.FormatPrice(goods.Price);
                    model.Price = priceModel;
                    #endregion
                }

                //picture
                if (preparePictureModel)
                {
                    #region Prepare goods picture

                    //If a size has been set in the view, we use it in priority
                    int pictureSize = goodsThumbPictureSize.HasValue ? goodsThumbPictureSize.Value : _mediaSettings.GoodsThumbPictureSize;
                    
                        var picture = goods.GetDefaultGoodsPicture(_pictureService);
                        var pictureModel = new MoPicture()
                        {
                            NormalUrl = _pictureService.GetPictureUrl(picture, pictureSize),
                            BigUrl = _pictureService.GetPictureUrl(picture)
                        };
                        model.Picture = pictureModel;
                   

                    #endregion
                }

                //specs
                if (prepareGoodsParameters)
                {
                    //specs for comparing
                    model.Specs = PrepareGoodsSpecificationModel(goods);
                }

                if (prepareGoodsTag)
                {
                    for (int i = 0; i < goods.GoodsTags.Count; i++)
                    {
                        var pt = goods.GoodsTags.ToList()[i];
                        model.Tags.Add(new MoGoodsOverview.MoGoodsTag()
                        {
                            Id = pt.GoodsTag.Id,
                            Name = pt.GoodsTag.Name
                        });
                    }
                }

                models.Add(model);
            }
            return models;
        }

        [NonAction]
        protected IList<MoSpecification> PrepareGoodsSpecificationModel(Goods goods)
        {
            if (goods == null)
                throw new ArgumentNullException("goods");

                var model = _goodsParameterService.GetGoodsParameterMappingByGoodsId(goods.Id, null, true)
                   .Select(psa =>
                   {
                       return new MoSpecification()
                       {
                           Name = psa.GoodsParameterOption.Mapping.Name,
                           Value = !String.IsNullOrEmpty(psa.CustomValue) ? psa.CustomValue : psa.GoodsParameterOption.Name,
                       };
                   }).ToList();
                return model;
        }

        [NonAction]
        protected MoGoods PrepareGoodsDetailsPageModel(Goods goods)
        {
            if (goods == null)
                throw new ArgumentNullException("goods");

            #region Standard properties

            var model = new MoGoods();

            model.Id = goods.Id;
            model.Name = goods.Name;
            model.Short = goods.ShortDescription;
            model.Full = goods.FullDescription;
            model.RatingSum = goods.ApprovedRatingSum;
            model.TotalReviews = goods.ApprovedTotalReviews;
            model.StockQuantity = goods.StockQuantity;
            model.Published = goods.Published;
            model.SaleVolume = goods.SaleVolume;
            model.IconTip = _storeInformationSettings.IconTip;
            model.ServiceTip = _storeInformationSettings.ServiceTip;
            decimal oldPrice = goods.OldPrice;
            decimal price = _priceCalculationService.GetFinalPrice(goods);
            model.Price = PriceFormatter.FormatPrice(price);
            if (price != oldPrice && oldPrice > decimal.Zero)
                model.OldPrice = PriceFormatter.FormatPrice(oldPrice);
            model.ProductCost = PriceFormatter.FormatPrice(goods.ProductCost);
            #endregion

            #region SpecAttrs

            model.Specs = PrepareGoodsSpecificationModel(goods);

            #endregion

            #region Pictures

            //pictures
            model.Pictures = new List<MoPicture>();
            var pictures = _pictureService.GetPicturesByGoodsId(goods.Id);
            if (pictures.Count > 0)
            {
                foreach (var picture in pictures)
                {
                    model.Pictures.Add(new MoPicture()
                    {
                        NormalUrl = _pictureService.GetPictureUrl(picture, _mediaSettings.GoodsThumbPictureSizeOnGoodsDetailsPage),
                        BigUrl = _pictureService.GetPictureUrl(picture, _mediaSettings.GoodsDetailsPictureSize)
                    });
                }
            }
            #endregion

            #region Goods spec

            var goodsAttrMaps = _goodsSpecService.GetGoodsSpecMappingByGoodsId(goods.Id);
            if (goodsAttrMaps.Count > 0)
                foreach (var map in goodsAttrMaps)
                {
                    if (model.Attrs.FirstOrDefault(pa => pa.GoodsSpecId == map.GoodsSpecId) == null)
                    {
                        var pa = new MoGoods.MoGoodsAttr()
                        {
                            Id = map.Id,
                            GoodsId = goods.Id,
                            GoodsSpecId = map.GoodsSpecId,
                            Name = map.GoodsSpecName
                        };
                        model.Attrs.Add(pa);
                    }
                    var pAttr = model.Attrs.FirstOrDefault(pa => pa.GoodsSpecId == map.GoodsSpecId);
                    var pvaValue = _goodsSpecService.GetGoodsSpecValueById(map.GoodsSpecValueId);
                    if (pvaValue == null) continue;

                    var valueItem = new MoGoods.MoGoodsAttrValue()
                    {
                        Id = pvaValue.Id,
                        Name = pvaValue.Name
                    };
                    pAttr.Values.Add(valueItem);
                }

            var goodsSpecCombinations = _goodsSpecService.GetAllGoodsSpecCombinations(goods.Id);
            model.AttrValueList = goodsSpecCombinations
                .Select(x =>
                {
                    var parsedGoodsVariantAttributes = _goodsSpecParser.ParseGoodsSpecValues(x.AttributesXml);
                    string attrs = "combination";
                    foreach (var i in parsedGoodsVariantAttributes)
                    {
                        attrs += "_" + i.Id.ToString();
                    }
                    var pvacModel = new MoGoods.MoGoodsAttrValueList()
                    {
                        Id = x.Id,
                        GoodsId = x.GoodsId,
                        AttributeValue = attrs,
                        AttributesXml = _goodsSpecFormatter.FormatAttributes(x.AttributesXml, ":", true, true, true, false),
                        Qty = x.StockQuantity,
                        Sku = x.Sku,
                        Price = PriceFormatter.FormatPrice(x.Price)
                    };

                    return pvacModel;
                })
                .ToList();

            #endregion

            #region Goods tag


            for (int i = 0; i < goods.GoodsTags.Count; i++)
            {
                var pt = goods.GoodsTags.ToList()[i];
                model.Tags.Add(new MoGoods.MoGoodsTag()
                {
                    Id = pt.GoodsTag.Id,
                    Name = pt.GoodsTag.Name
                });
            }

            #endregion

            return model;
        }
        #endregion

        /// <summary>
        /// 按分类Id获取推荐商品
        /// </summary>
        /// <param name="cid">分类Id</param>
        /// <param name="count">商品数量</param>
        /// <param name="picsize">图片大小(px)(非必填)</param>
        /// <param name="specshow">显示参数</param>
        /// <param name="tagshow">显示标签</param>
        /// <param name="fullshow">显示内容</param>
        /// <returns></returns>
        [HttpGet("featuredbycid")]
        public async Task<ApiResponse<List<MoGoodsOverview>>> GetFeaturedGoodssByCId(int cid, int count, int? picsize,
            bool specshow = false, bool tagshow = false, bool fullshow = false)
        {
            var data = await Task.Run(() =>
            {
                var flist = _goodsService.GetFeaturedGoodsByCategoryId(cid, count);

                return PrepareGoodsOverviewModels(flist, true, true, picsize,
                    prepareGoodsParameters: specshow, prepareGoodsTag: tagshow, prepareFullDescription: fullshow).ToList();
            });

            return ApiResponse<List<MoGoodsOverview>>.Success(data);
        }

        /// <summary>
        /// 首页推荐商品
        /// </summary>
        /// <param name="picsize">图片大小(px)(非必填)</param>
        /// <param name="specshow">显示参数</param>
        /// <param name="tagshow">显示标签</param>
        /// <param name="fullshow">显示内容</param>
        /// <returns></returns>
        [HttpGet("homegoods")]
        public async Task<ApiResponse<List<MoGoodsOverview>>> GetHomeGoods(int? picsize,
            bool specshow = false, bool tagshow = false, bool fullshow = false)
        {
            var data = await Task.Run(() =>
            {
                var list = _goodsService.GetAllGoodssDisplayedOnHomePage();
                return PrepareGoodsOverviewModels(list, true, true, picsize,
                       prepareGoodsParameters: specshow, prepareGoodsTag: tagshow, prepareFullDescription: fullshow).ToList();
            });

            return ApiResponse<List<MoGoodsOverview>>.Success(data);
        }

        /// <summary>
        /// 促销商品
        /// </summary>
        /// <param name="count">商品数量</param>
        /// <param name="picsize">图片大小(px)(非必填)</param>
        /// <param name="specshow">显示参数</param>
        /// <param name="tagshow">显示标签</param>
        /// <param name="fullshow">显示内容</param>
        /// <returns></returns>
        [HttpGet("specialgoods")]
        public async Task<ApiResponse<List<MoGoodsOverview>>> GetSpecialGoods(int count, int? picsize,
             bool specshow = false, bool tagshow = false, bool fullshow = false)
        {
            var data = await Task.Run(() =>
            {
                var list = _goodsService.GetSpecialGoodss(count);

                return PrepareGoodsOverviewModels(list, true, true, picsize,
                    prepareGoodsParameters: specshow, prepareGoodsTag: tagshow, prepareFullDescription: fullshow).ToList();
            });

            return ApiResponse<List<MoGoodsOverview>>.Success(data); ;
        }
        /// <summary>
        /// 买了又买的商品
        /// </summary>
        /// <param name="pId">商品Id</param>
        /// <param name="count">商品数量</param>
        /// <param name="picsize">图片缩略图大小(px)(非必填)</param>
        /// <param name="specshow">显示参数</param>
        /// <param name="tagshow">显示标签</param>
        /// <param name="fullshow">显示内容</param>
        /// <returns></returns>
        [HttpGet("alsopurchased")]
        public async Task<ApiResponse<List<MoGoodsOverview>>> AlsoPurchased(int pId, int count, int? picsize,
             bool specshow = false, bool tagshow = false, bool fullshow = false)
        {
            var data = await Task.Run(() =>
            {
                //load and cache report
                var ids =  _orderReportService.GetGoodssAlsoPurchasedById(pId, count)
                        .Select(x => x.Id).ToArray();
                //load list
                var list = _goodsService.GetGoodssByIds(ids);
                //prepare model
                return PrepareGoodsOverviewModels(list, true, true, picsize,
                    prepareGoodsParameters: specshow, prepareGoodsTag: tagshow, prepareFullDescription: fullshow).ToList();
            });
            return ApiResponse<List<MoGoodsOverview>>.Success(data);
        }


        /// <summary>
        /// 获取标签列表
        /// </summary>
        /// <param name="count">显示数量(默认：所有)</param>
        /// <returns></returns>
        [HttpGet("taglist")]
        public async Task<ApiResponse<List<MoGoodsTag>>> GetTagList(int count = int.MaxValue)
        {
            var taglist = _goodsTagService.GetAllGoodsTags().Take(count);

            var list = new List<MoGoodsTag>();
            foreach (var item in taglist)
            {
                list.Add(new MoGoodsTag()
                {
                    Id = item.Id,
                    Name = item.Name
                });
            }

            return ApiResponse<List<MoGoodsTag>>.Success(list);
        }

        /// <summary>
        /// 根据标签查找商品
        /// </summary>
        /// <param name="tagName">标签名称</param>
        /// <param name="count">商品数量</param>
        /// <param name="picsize">图片缩略图大小(px)(非必填)</param>
        /// <param name="specshow">显示参数</param>
        /// <param name="tagshow">显示标签</param>
        /// <param name="fullshow">显示内容</param>
        /// <returns></returns>
        [HttpGet("getbytag")]
        public async Task<ApiResponse<List<MoGoodsOverview>>> GetByTag(string tagName, int count, int? picsize,
             bool specshow = false, bool tagshow = false, bool fullshow = false)
        {
            var goodsTag = _goodsTagService.GetGoodsTagByName(tagName);
            if (goodsTag == null)
                return ApiResponse<List<MoGoodsOverview>>.NotFound();
            var data = await Task.Run(() =>
            {
                var list = _goodsService.GetGoodsByTag(goodsTag.Id, count: count);
                //prepare model
                return PrepareGoodsOverviewModels(list, true, true, picsize,
                    prepareGoodsParameters: specshow, prepareGoodsTag: tagshow, prepareFullDescription: fullshow).ToList();
            });
            return ApiResponse<List<MoGoodsOverview>>.Success(data);
        }
        /// <summary>
        /// 商品多规格
        /// </summary>
        /// <param name="pId">商品Id</param>
        /// <returns></returns>
        [HttpGet("getgoodsattr")]
        public async Task<ApiResponse<MoGoodsAttrList>> GetGoodsAttr(int pId)
        {
            var goods = _goodsService.GetGoodsById(pId);
            if (goods == null || goods.Deleted)
                return ApiResponse<MoGoodsAttrList>.NotFound();

            var model = new MoGoodsAttrList();
            var pp = _goodsSpecService.GetGoodsSpecMappingByGoodsId(goods.Id);
            foreach (var p in pp)
            {
                var attr = model.attrs.FirstOrDefault(q => q.value == p.GoodsSpecId);
                if (attr == null)
                {
                    attr = new MoGoodsAttr() { value = p.GoodsSpecId, name = p.GoodsSpecName };
                    model.attrs.Add(attr);

                }

                var attrValue = _goodsSpecService.GetGoodsSpecValueById(p.GoodsSpecValueId);
                if (attr.attrValArr.FirstOrDefault(ava => ava.value == attrValue.Id) == null)
                    attr.attrValArr.Add(new MoGoodsAttr.GoodsAttrValue() { value = attrValue.Id, name = attrValue.Name });
            }

            var combinations = _goodsSpecService.GetAllGoodsSpecCombinations(goods.Id);
            var list = new List<string>();
            list.Add("https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1391420499,739011976&fm=15&gp=0.jpg");
            list.Add("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=3102734236,2715809020&fm=26&gp=0.jpg");
            var index = 0;
            foreach (var item in combinations)
            {
                var at = new MoAttrTable();
                at.info.price = item.Price;
                at.info.qty = item.StockQuantity;
                at.info.sku = item.Sku;
                at.info.img = index % 2 == 0 ? list[0] : list[1];
                var values = _goodsSpecParser.ParseGoodsSpecValues(item.AttributesXml);
                foreach (var value in values)
                {
                    at.arrs.Add(new MoAttrTable.AttrValueItem()
                    {
                        value = value.Id,
                        name = value.Name

                    });
                }
                model.tables.Add(at);

                index++;
            }

            return ApiResponse<MoGoodsAttrList>.Success(model);
        }

        /// <summary>
        /// 商品参数
        /// </summary>
        /// <param name="pId">商品Id</param>
        /// <returns></returns>
        [HttpGet("getgoodsspec")]
        public async Task<ApiResponse<List<MoGoodsSpec>>> GetGoodsSpec(int pId)
        {
            var goods = _goodsService.GetGoodsById(pId);
            if (goods == null || goods.Deleted)
                return ApiResponse<List<MoGoodsSpec>>.NotFound();

            var model = new List<MoGoodsSpec>();
            var pp = _goodsParameterService.GetGoodsParameterMappingByGoodsId(goods.Id);
            model = pp.Select(p =>
            {
                var option = p.GoodsParameterOption;
                var spec = p.GoodsParameterOption.Mapping;
                var ps = new MoGoodsSpec { id = spec.Id, name = spec.Name, valid = option.Id, valname = option.Name };
                return ps;
            }).ToList();

            return ApiResponse<List<MoGoodsSpec>>.Success(model);
        }


        /// <summary>
        /// 获取单个商品详情
        /// </summary>
        /// <param name="id">商品Id</param>
        /// <returns></returns>
        [HttpGet("getbyid")]
        public async Task<ApiResponse<MoGoods>> Get(int id)
        {
            var goods = _goodsService.GetGoodsById(id);
            if (goods == null || goods.Deleted)
                return ApiResponse<MoGoods>.NotFound();

            //prepare the model
            var model = PrepareGoodsDetailsPageModel(goods);

            return ApiResponse<MoGoods>.Success(model);
        }


        /// <summary>
        /// 获取单个商品详情[基本没用]
        /// </summary>
        /// <param name="sku">商品Sku</param>
        /// <returns></returns>
        [HttpGet("getbysku")]
        public async Task<ApiResponse<MoGoods>> Get(string sku)
        {
            var goods = _goodsService.GetGoodsBySku(sku);
            if (goods == null || goods.Deleted)
                return ApiResponse<MoGoods>.NotFound();

            if (!goods.Published && !_permissionService.Authorize(StandardPermissionProvider.ManageCatalog))
                return ApiResponse<MoGoods>.NotFound();

            //prepare the model
            var model = PrepareGoodsDetailsPageModel(goods);

            return ApiResponse<MoGoods>.Success(model);
        }
        /// <summary>
        /// 提交商品[Auth]
        /// </summary>
        /// <returns></returns>
        [ApiAuthorize]
        [HttpPost("post")]
        public async Task<ApiResponse<int>> Submit(MoGoodsRequest mo)
        {
            var user = RegisterUser;
            if (ModelState.IsValid)
            {
                var goods = new Goods();
                goods.Name = mo.Name;
                goods.Price = mo.Price;
                goods.ShortDescription = mo.Short;
                goods.FullDescription = mo.Full;
                goods.Published = true;
                goods.IsShipEnabled = false;
                goods.CreateTime = DateTime.Now;
                goods.UpdateTime = DateTime.Now;
                _goodsService.InsertGoods(goods);
                foreach (var item in mo.PictureIds)
                {
                    _goodsService.InsertGoodsPicture(new GoodsPicture()
                    {
                        PictureId = item,
                        GoodsId = goods.Id,
                    });
                }

                //find next display order
                int displayOrder = 1;
                var categoryMapping = _categoryService.GetGoodsCategoriesByCategoryId(mo.CategoryId, 0, int.MaxValue, showHidden: true);
                if (categoryMapping.Any())
                    displayOrder = categoryMapping.Max(x => x.DisplayOrder) + 1;
                if (_categoryService.GetCategoryById(mo.CategoryId) != null)
                    _categoryService.InsertGoodsCategory(new GoodsCategory
                    {
                        GoodsId = goods.Id,
                        CategoryId = mo.CategoryId,
                        DisplayOrder = displayOrder
                    });

                foreach (string goodsTagName in mo.Tags)
                {
                    var goodsTag = _goodsTagService.GetGoodsTagByName(goodsTagName);
                    if (goodsTag == null)
                    {
                        //add new goods tag
                        goodsTag = new GoodsTag()
                        {
                            Name = goodsTagName,
                            Count = 0
                        };
                        _goodsTagService.InsertGoodsTag(goodsTag);
                    }
                    if (!goods.GoodsTagExists(goodsTag.Id))
                    {
                        goods.GoodsTags.Add(new GoodsTagMapping { GoodsTag = goodsTag });
                        //ensure goods is saved before updating totals
                        _goodsService.UpdateGoods(goods);
                    }
                    //update goods tag totals 
                    _goodsTagService.UpdateGoodsTagTotals(goodsTag);
                }
                return ApiResponse<int>.Success(goods.Id);
            }

            return ApiResponse<int>.Warn(ModelState.FirstMessage());
        }
    }
}
